""" Game actions

Actions are executes for a hex.

"""
import logging

import pyciv.combat
import pyciv.unit

class Action(object):
    def __init__(self, name, hex=None, 
                 attribute_costs = [], 
                 enabled=True, reason="Select hexagon"):
        self.name = name
        self.hex = hex
        self.enabled = enabled
        self.attribute_costs = attribute_costs
        self.reason = reason
        self.value = 0
        for provider, attributes in self.attribute_costs:
            for attribute, value in attributes.iteritems():
                if provider.attributes[attribute] < value:
                    self.enabled = False


    def evaluate(self, player, income, engine):
        pass

    def execute(self, engine):
        if self.enabled:
            for provider, attributes in self.attribute_costs:
                for attribute, value in attributes.iteritems():
                    provider.attributes[attribute] -= value
            self._execute(engine)

    def _execute(self, engine):
        pass

    def __str__(self):
        return str((self.name, self.value))

NONE = Action("")

class Move(Action):
    def __init__(self, hex, engine):
        reason = "Move %d AP, %d supply" % (
            engine.state.terrain[hex].cost,
            engine.state.unit[engine.current_hex].supply_needed_to_move())
        Action.__init__(self, "Move", hex=hex, 
                        attribute_costs=[(engine.state.unit[engine.current_hex], 
                                          {pyciv.attribute.ACTIONPOINTS:engine.state.terrain[hex].cost,
                                           pyciv.attribute.DEFENCE:engine.state.unit[engine.current_hex].attributes[pyciv.attribute.DEFENCE]}),
                                         (engine.current_player(),
                                          {pyciv.attribute.SUPPLY:engine.state.unit[engine.current_hex].supply_needed_to_move()})], 
                        reason=reason)
        
    def _execute(self, engine):
        unit = engine.state.unit[engine.current_hex]
        engine.state.unit[self.hex] = unit
        engine.state.owner[self.hex] = engine.current_player()
        engine.state.unit[engine.current_hex] = pyciv.unit.NONE

    def evaluate(self, player, income, engine):
        return 0

class Switch(Action):
    def __init__(self, hex, engine):
        total_supply = engine.state.unit[engine.current_hex].supply_needed_to_move() + \
            engine.state.unit[hex].supply_needed_to_move()

        reason = "Switch %d AP, %d AP, Supply: %d" % (
            engine.state.terrain[hex].cost,
            engine.state.terrain[engine.current_hex].cost,
            total_supply)
        Action.__init__(self, "Switch", hex=hex, 
                        attribute_costs=[(engine.state.unit[engine.current_hex], 
                                          {pyciv.attribute.ACTIONPOINTS:engine.state.terrain[hex].cost,
                                           pyciv.attribute.DEFENCE:engine.state.unit[engine.current_hex].attributes[pyciv.attribute.DEFENCE]}),
                                         (engine.state.unit[hex], 
                                          {pyciv.attribute.ACTIONPOINTS:engine.state.terrain[engine.current_hex].cost,
                                           pyciv.attribute.DEFENCE:engine.state.unit[hex].attributes[pyciv.attribute.DEFENCE]}),
                                         (engine.current_player(),
                                          {pyciv.attribute.SUPPLY:total_supply})], 
                        reason=reason)
        
        
    def _execute(self, engine):
        unit = engine.state.unit[self.hex]
        engine.state.unit[self.hex] = engine.state.unit[engine.current_hex]
        engine.state.unit[engine.current_hex] = unit

class Attack(Action):
    def __init__(self, hex, engine):
        estimate = pyciv.combat.estimate(engine.state.unit[engine.current_hex], 
                                         engine.state.unit[hex])
        reason = "Attack %d AP %d SP, Kill: %.1f, Lose: %.1f" % (
            engine.state.terrain[hex].cost,
            engine.state.unit[engine.current_hex].supply_needed_to_move(),
            estimate[1],
            estimate[0])
        Action.__init__(self, "Attack", hex=hex, 
                        attribute_costs=[(engine.state.unit[engine.current_hex], 
                                          {pyciv.attribute.ACTIONPOINTS:engine.state.terrain[hex].cost,
                                           pyciv.attribute.DEFENCE:engine.state.unit[engine.current_hex].attributes[pyciv.attribute.DEFENCE]}),
                                         (engine.current_player(),
                                          {pyciv.attribute.SUPPLY:engine.state.unit[engine.current_hex].supply_needed_to_move()})], 
                        reason=reason)

    def _execute(self, engine):
        result = pyciv.combat.Result(engine.state.unit[engine.current_hex],
                                     engine.state.unit[self.hex])

        # Attack
        counter = 0
        while(counter < 3):
            losses = pyciv.combat.attack(engine.state.unit[engine.current_hex], 
                                     engine.state.unit[self.hex])

            pyciv.combat.handle_losses(engine.state.unit[engine.current_hex], losses[0])
            pyciv.combat.handle_losses(engine.state.unit[self.hex], losses[1])
            result.losses(losses[0], losses[1])

            if engine.state.unit[self.hex].alive():        
                # Check retreat
                estimate = pyciv.combat.estimate(engine.state.unit[engine.current_hex], 
                                                 engine.state.unit[self.hex])
                kill_rate = estimate[0] / estimate[1]
                logging.debug("Retreat: %s, Kill rate: %.2f" % (str(estimate), kill_rate))
                if kill_rate < 0.8:
                    for neighbour in pyciv.hexagon.get_neighbours(self.hex):
                        if engine.state.owner[neighbour] == engine.state.owner[self.hex] and engine.state.unit[neighbour] == pyciv.unit.NONE:
                            engine.state.unit[neighbour] = engine.state.unit[self.hex]
                            engine.state.unit[self.hex] = pyciv.unit.NONE
                            
            else:
                # Killed
                engine.state.unit[self.hex] = pyciv.unit.NONE
        
            if engine.state.unit[engine.current_hex].alive():
                # Check advance
                if engine.state.unit[self.hex] == pyciv.unit.NONE:
                    engine.state.unit[self.hex] = engine.state.unit[engine.current_hex]
                    engine.state.owner[self.hex] = engine.state.owner[engine.current_hex]
                    engine.state.unit[engine.current_hex] = pyciv.unit.NONE
                    break
                else:
                    # Keep attacking?
                    estimate = pyciv.combat.estimate(engine.state.unit[engine.current_hex], 
                                                     engine.state.unit[self.hex])
                    kill_rate = estimate[0] / estimate[1]
                    logging.debug("Keep attack: %s, Kill rate: %.2f" % (str(estimate), kill_rate))
                    if kill_rate > 1.2:
                        break
            else:
                # Killed
                engine.state.unit[engine.current_hex] = pyciv.unit.NONE
                break
            counter += 1
            
        engine.combat_results.append(result)


class Defend(Action):
    def __init__(self, hex, engine):
        max_points = (engine.state.unit[hex].strength() - engine.state.unit[hex].attributes[pyciv.attribute.DEFENCE])
        
        self.points = engine.state.unit[hex].attributes[pyciv.attribute.ACTIONPOINTS] / 3
        if self.points + engine.state.unit[hex].attributes[pyciv.attribute.DEFENCE] > max_points:
            self.points = max_points - engine.state.unit[hex].attributes[pyciv.attribute.DEFENCE]

        Action.__init__(self, "Defend", hex=hex, 
                        attribute_costs=[(engine.state.unit[hex], 
                                          {pyciv.attribute.ACTIONPOINTS:self.points * 3}),
                                         (engine.current_player(),
                                          {pyciv.attribute.SUPPLY:self.points * 3})],
                        enabled=self.points>0,
                        reason="Dig defences, %d AP, %d supply" % (self.points * 3, self.points * 3))

    def _execute(self, engine):
        engine.state.unit[self.hex].attributes[pyciv.attribute.DEFENCE] += self.points

class Reserve(Action):
    def __init__(self, hex, engine, attribute):
        self.attribute = attribute
        ap_cost = engine.state.unit[hex].attributes[pyciv.attribute.ACTIONPOINTS]
        supply_cost = 30

        Action.__init__(
            self, str(attribute), hex=hex, 
            attribute_costs=[(engine.state.unit[hex], 
                              {pyciv.attribute.ACTIONPOINTS:ap_cost}),
                             (engine.current_player(),
                              {pyciv.attribute.SUPPLY:supply_cost})],
            enabled=engine.state.unit[hex].strength()<pyciv.rules.MAX_STRENGTH,
            reason="Gain 1 %s, %d AP, %d supply" % (
                str(self.attribute), ap_cost, supply_cost))

    def _execute(self, engine):
        engine.state.unit[self.hex].attributes[self.attribute] += 1
